﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LC3虚拟机
{
    internal class Program
    {
        public enum InstructionSet : short
        {
            OP_BR = 0, // 条件分支
            OP_ADD = 1, // 加法
            OP_LD = 2, // 取内存地址的值到寄存器
            OP_ST = 3, // 存寄存器的值到内存地址
            OP_JSR = 4, // 跳转函数
            OP_AND = 5, // 与运算
            OP_LDR = 6, // 取值到寄存器
            OP_STR = 7, // 存储寄存器的值
            OP_RTI = 8, // unused
            OP_NOT = 9, // 取反
            OP_LDI = 10, // 间接取值
            OP_STI = 11, // 间接存值
            OP_JMP = 12, // 跳转
            OP_RES = 13, // reserved
            OP_LEA = 14, // 加载地址到寄存器
            OP_TRAP = 15, // 系统调用
        }

        public class ConditionFlags
        {
            private ConditionFlags() { }

            private class Inner
            {
                private Inner() { }

                static internal readonly ConditionFlags registers = new ConditionFlags();
            }
            public static ConditionFlags Instance
            {
                get

                    => Inner.registers;

            }
            public Dictionary<string, object> RegisterKey = new Dictionary<string, object>()
            {
                { "Postive", (short )1 << 0 },
                { "Zore", (short )1 << 1 },
                { "Negative",(short )1 << 2 },
            };
            public static string Postive = "Postive",
                Zore = "Zore",
                Negative = "Negative";
        }

        public class Registers
        {
            private Registers() { }

            private class Inner
            {
                private Inner() { }

                static internal readonly Registers registers = new Registers();
            }

            public static Registers Instance => Inner.registers;
            public Dictionary<string, object> RegisterKey = new Dictionary<string, object>()
            {
                { "R0", 0 },
                { "R1", 2 },
                { "R2", 0 },
                { "R3", 0 },
                { "R4", 0 },
                { "R5", 0 },
                { "R6", 0 },
                { "R7", 3 },
            };

            public static string GetRegisterKey(int key)
            {
                string retuslt = Registers.R0;
                switch (key)
                {
                    case 0:
                        retuslt = Registers.R0;
                        break;
                    case 1:
                        retuslt = Registers.R1;
                        break;
                    case 2:
                        retuslt = Registers.R2;
                        break;
                    case 3:
                        retuslt = Registers.R3;
                        break;
                    case 4:
                        retuslt = Registers.R4;
                        break;
                    case 5:
                        retuslt = Registers.R5;
                        break;
                    case 6:
                        retuslt = Registers.R6;
                        break;
                    case 7:
                        retuslt = Registers.R7;
                        break;
                }
                return retuslt;
            }

            public static string R0 = "R0",
                R1 = "R1",
                R2 = "R2",
                R3 = "R3",
                R4 = "R4",
                R5 = "R5",
                R6 = "R6",
                R7 = "R7";
            public static int PC = 0;
            public static int SP = -1;
            public static int COND = 0;
        }
        // 类型
        public enum TrapSet
        {
            TRAP_GETC = 0x20, // 从键盘输入
            TRAP_OUT = 0x21,  //输出字符
            TRAP_PUTS = 0x22, // 输出字符串
            TARP_IN = 0x23,  // 打印输入提示，读取单个字符
            TRAP_PUTSP = 0x24,// 输出字符串
            TRAP_HALT = 0x25, // 退出程序
        }

        public static List<object> MemoryArrary = new List<object>();
        public static Stack<object> StackOption = new Stack<object>();
        static int OpCode = 0;
        static InstructionSet opSet;
        static List<int> OpCodeList = new List<int>() {
            0b1111_0000_0010_0000,
             0b0011_001_000_0_01111,
             0b1011_001_000_0_01110,
           0b0001_000_001_0_00010,
            

    // ADD R1,R2,#6
    0b0001_001_010_1_00110,

    // JSR 2;
    // 下一条是第 4 条指令，PC=3，跳转到 PC=3+2，也就是第 6 条指令 RES。
   0b0100100000000010,

    // JMP R1;跳转到第 6 条指令
      (0b1100_000_001_000000),

    // RTI;占位
       (0X8000),

    // RES;占位
   ( 0XD000),

    // NOT R3, R1
     (0x967F),

    // trap-halt
     (0xf000), };
        static bool isjmp = false;
        static bool runing = true;
        static void Main(string[] args)
        {
            for (int i = 0; i < 6653; i++)
            {
                OpCodeList.Add(0);
            }

            while (runing && Registers.PC < OpCodeList.Count)
            {
                var child = OpCodeList[Registers.PC];
                OpCode = child;
                opSet = (InstructionSet)GetSome(OpCode, 12, 4);
                if (!isjmp)
                {
                    Registers.PC += 1;
                }
                Evel(opSet);
            }
        }

        static void UpFlags(string r)
        {
            var g = (int)Registers.Instance.RegisterKey[r];
            if (g == 0)
            {
                Registers.COND = Convert.ToInt32(ConditionFlags.Instance.RegisterKey[ConditionFlags.Zore]);
            }
            else if (GetOne(g, 15) == 1)
            {
                Registers.COND = Convert.ToInt32(ConditionFlags.Instance.RegisterKey[ConditionFlags.Negative]);
            }
            else
            {
                Registers.COND = Convert.ToInt32(ConditionFlags.Instance.RegisterKey[ConditionFlags.Postive]);
            }
        }

        static void Evel(InstructionSet op)
        {
            runing = true;
            isjmp = false;
            switch (op)
            {
                case InstructionSet.OP_BR:
                    var br_pzn = GetSome(OpCode, 9, 3);
                    var br_s = Sign_Extend(OpCode, 9);
                    if ((br_pzn & Convert.ToInt16(Registers.COND)) == 1)
                    {
                        Registers.PC += br_s;
                    }
                    break;
                case InstructionSet.OP_ADD:
                    var add_f = GetOne(OpCode, 6);
                    var add_d = GetSome(OpCode, 9, 3);
                    var add_s = GetSome(OpCode, 6, 3);
                    var add_right = add_f == 1
                        ?
                        Sign_Extend(OpCode, 5)
                        :
                        Convert.ToInt16(Registers.Instance.RegisterKey[Registers.GetRegisterKey(GetSome(OpCode, 0, 3))]);
                    var add_left = Convert.ToInt32(Registers.Instance.RegisterKey[Registers.GetRegisterKey(add_s)]);
                    short add_v;
                    if (add_f == 1)
                    {
                        add_v = (short)(add_left + add_right);
                    }
                    else
                    {
                        add_v = (short)(add_left + add_right);
                    }
                    Registers.Instance.RegisterKey[Registers.GetRegisterKey(add_d)] = add_v;
                    UpFlags(Registers.GetRegisterKey(add_d));
                    break;
                case InstructionSet.OP_LD:
                    var ld_r = GetSome(OpCode, 9, 3);
                    var ld_f = OpCodeList[Registers.PC + Sign_Extend(OpCode, 9)];
                    Registers.Instance.RegisterKey[Registers.GetRegisterKey(ld_r)] = ld_f;
                    UpFlags(Registers.GetRegisterKey(ld_r));
                    break;
                case InstructionSet.OP_ST:
                    var st_r = GetSome(OpCode, 9, 3);
                    var st_r_v = (int)Registers.Instance.RegisterKey[Registers.GetRegisterKey(st_r)];
                    var st_add = Registers.PC + Sign_Extend(OpCode, 9);
                    OpCodeList.Insert(st_add, st_r_v);
                    break;
                case InstructionSet.OP_JSR:
                    Registers.Instance.RegisterKey[Registers.R7] = Registers.PC;
                    var jsr_o = GetOne(OpCode, 12);
                    if (jsr_o == 1)
                    {
                        var jsr_se = Sign_Extend(OpCode, 11);
                        Registers.PC += jsr_se;
                    }
                    else
                    {
                        Registers.PC = GetSome(OpCode, 6, 3);
                    }
                    isjmp = true;
                    break;
                case InstructionSet.OP_AND:
                    var and_f = GetOne(OpCode, 6);
                    var and_d = GetSome(OpCode, 9, 3);
                    var and_s = GetSome(OpCode, 6, 3);
                    var and_right = and_f == 1
                        ?
                        Sign_Extend(OpCode, 5)
                        :
                        Convert.ToInt16(Registers.Instance.RegisterKey[Registers.GetRegisterKey(GetSome(OpCode, 0, 3))]);
                    var and_left = Convert.ToInt16(Registers.Instance.RegisterKey[Registers.GetRegisterKey(and_s)]);
                    short and_v;
                    if (and_f == 1)
                    {
                        and_v = (short)(and_left & and_right);
                    }
                    else
                    {
                        and_v = (short)(and_left & and_right);
                    }
                    Registers.Instance.RegisterKey[Registers.GetRegisterKey(and_d)] = and_v;
                    UpFlags(Registers.GetRegisterKey(and_d));
                    break;
                case InstructionSet.OP_LDR:
                    var ldr_r = GetSome(OpCode, 9, 3);
                    var ldr_f = (int)Registers.Instance.RegisterKey[(Registers.GetRegisterKey(GetSome(OpCode, 6, 3)))];
                    var ldr_se = Sign_Extend(OpCode, 5);
                    var ldr_add = ldr_f + ldr_se;
                    Registers.Instance.RegisterKey[(Registers.GetRegisterKey(ldr_r))] = OpCodeList[ldr_add];
                    UpFlags(Registers.GetRegisterKey(ldr_r));
                    break;
                case InstructionSet.OP_STR:
                    var str_r = GetSome(OpCode, 9, 3);
                    var str_f = (int)Registers.Instance.RegisterKey[(Registers.GetRegisterKey(GetSome(OpCode, 6, 3)))];
                    var str_se = Sign_Extend(OpCode, 5);
                    var str_add = str_f + str_se;
                    OpCodeList[str_add] = (int)Registers.Instance.RegisterKey[(Registers.GetRegisterKey(str_r))];
                    break;
                case InstructionSet.OP_RTI:
                    break;
                case InstructionSet.OP_NOT:
                    var not_d = GetSome(OpCode, 9, 3);
                    var not_s = GetSome(OpCode, 6, 3);
                    var not_left = Convert.ToInt16(Registers.Instance.RegisterKey[Registers.GetRegisterKey(not_s)]);
                    var not_v = ~not_left;
                    Registers.Instance.RegisterKey[Registers.GetRegisterKey(not_d)] = not_v;
                    UpFlags(Registers.GetRegisterKey(not_d));
                    break;
                case InstructionSet.OP_LDI:
                    var ldi_r = GetSome(OpCode, 9, 3);
                    var ldi_add = Registers.PC + Sign_Extend(OpCode, 9);
                    var ldi_true_add = OpCodeList[ldi_add];
                    Registers.Instance.RegisterKey[Registers.GetRegisterKey(ldi_r)] = OpCodeList[ldi_true_add];
                    UpFlags(Registers.GetRegisterKey(ldi_r));
                    break;
                case InstructionSet.OP_STI:
                    var sti_r = GetSome(OpCode, 9, 3);
                    var sti_r_v = (int)Registers.Instance.RegisterKey[Registers.GetRegisterKey(sti_r)];
                    var sti_add = Registers.PC + Sign_Extend(OpCode, 9);
                    var sti_true_add = OpCodeList[sti_add];
                    OpCodeList[sti_true_add] = sti_r_v;
                    break;
                case InstructionSet.OP_JMP:
                    var jmp_left = Convert.ToInt16(Registers.Instance.RegisterKey[Registers.GetRegisterKey(GetSome(OpCode, 6, 3))]);
                    break;
                case InstructionSet.OP_RES:
                    break;
                case InstructionSet.OP_LEA:
                    var lea_r = GetSome(OpCode, 9, 3);
                    var lea_se = Registers.PC + Sign_Extend(OpCode, 9);
                    Registers.Instance.RegisterKey[Registers.GetRegisterKey(lea_r)] = lea_se;
                    UpFlags(Registers.GetRegisterKey(lea_r));
                    break;
                case InstructionSet.OP_TRAP:
                    var gettrapset = (TrapSet)GetSome(OpCode, 0, 8);
                    switch (gettrapset)
                    {
                        case TrapSet.TRAP_GETC:
                            Registers.Instance.RegisterKey[Registers.R0] = Console.Read();
                            break;
                        case TrapSet.TRAP_OUT:
                            Console.Write(Registers.Instance.RegisterKey[Registers.R0]);
                            break;
                        case TrapSet.TRAP_PUTS:
                            break;
                        case TrapSet.TARP_IN:
                            break;
                        case TrapSet.TRAP_PUTSP:
                            break;
                        case TrapSet.TRAP_HALT:
                            Console.Write("EXIT");
                            runing = false;
                            break;
                    }
                    break;
                default:
                    break;
            }
        }

        /// <summary>
        /// 右侧为0，取得二进制中几位数
        /// </summary>
        /// <param name="x">位数</param>
        /// <param name="index">右数起点,起点位置0</param>
        /// <param name="bit_count">几位</param>
        /// <returns></returns>
        static int GetSome(int x, int index, int bit_count)
        {
            //var g= Convert.ToString((x >> index), 2);
            x = (int)((x >> index) & ((int)Math.Pow(2, bit_count) - 1));
            return x;
        }

        static int GetOne(int x, int bit_count) => (x >> bit_count - 1) & 1;

        static int Sign_Extend(int y, int bit_count)
        {
            int g = (int)(0xFFFF << bit_count);
            var z = Convert.ToString(g, 2);
            if (GetOne(y, bit_count) == 0)
            {
                g = (int)~g;
                y &= g;
            }
            else
            {
                y |= g;
            }
            return y;
        }
    }
}
